package com.mjk.common.core.jdbc.service.plus.provider.abstractor;


import com.mjk.common.base.model.BaseEntity;
import com.mjk.common.core.jdbc.annotation.ArcSharding;
import com.mjk.common.core.jdbc.annotation.Id;
import com.mjk.common.core.jdbc.ibatis.typehandler.handler.*;
import com.mjk.common.core.jdbc.service.plus.constns.BeeTypeEntity;
import com.mjk.common.core.jdbc.service.plus.provider.BeeDataSourceType;
import com.mjk.common.core.jdbc.service.plus.provider.interfaces.IBeeProvider;
import com.mjk.common.core.jdbc.service.plus.provider.interfaces.IBeeTypeMapping;
import com.mjk.common.core.jdbc.util.TableUtils;
import com.mjk.common.core.jdbc.util.provider.InsertSqlProvider;
import com.mjk.common.core.jdbc.util.provider.SqlProvider;
import com.mjk.common.tools.logger.BizLogger;
import com.mjk.common.util.ObjectUtils;
import com.mjk.common.util.ReflectionUtils;
import com.mjk.common.util.StrUtils;
import com.mjk.common.util.TimeUtils;
import org.apache.ibatis.type.JdbcType;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.sql.Blob;
import java.sql.Clob;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

public abstract class AbstractSqlServerProvider implements IBeeProvider, IBeeTypeMapping {
    private Set<BeeTypeEntity> allMapping = new HashSet<>(Arrays.asList(
            new BeeTypeEntity(JdbcType.BIT, "BIT", Boolean.class.getName(), "4", 0, new BooleanHandler()),
            new BeeTypeEntity(JdbcType.TINYINT, "TINYINT", Byte.class.getName(), "4", 1, new ByteHandler()),
            new BeeTypeEntity(JdbcType.SMALLINT, "SMALLINT", Short.class.getName(), "4", 0, new ShortHandler()),
            new BeeTypeEntity(JdbcType.INTEGER, "INT", Integer.class.getName(), "11", 0, new IntegerHandler()),
            new BeeTypeEntity(JdbcType.BIGINT, "BIGINT", Long.class.getName(), "20", 1, new LongHandler()),
            new BeeTypeEntity(JdbcType.FLOAT, "FLOAT", Float.class.getName(), "1", 0, new FloatHandler()),
            new BeeTypeEntity(JdbcType.NUMERIC, "NUMERIC", Float.class.getName(), "1", 0, new FloatHandler()),
            new BeeTypeEntity(JdbcType.REAL, "FLOAT", Float.class.getName(), "0", 0, new FloatHandler()),
            new BeeTypeEntity(JdbcType.DOUBLE, "DOUBLE", Double.class.getName(), "10,2", 1, new DoubleHandler()),
            new BeeTypeEntity(JdbcType.DECIMAL, "DECIMAL", java.math.BigDecimal.class.getName(), "0", 0, new BigDecimalHandler()),
            new BeeTypeEntity(JdbcType.CHAR, "CHAR", String.class.getName(), "1", 0, new StringHandler()),
            new BeeTypeEntity(JdbcType.VARCHAR, "VARCHAR", String.class.getName(), "255", 1, new StringHandler()),
            new BeeTypeEntity(JdbcType.NVARCHAR, "NVARCHAR", String.class.getName(), "255", 1, new StringHandler()),
            new BeeTypeEntity(JdbcType.LONGVARCHAR, "LONGVARCHAR", String.class.getName(), "255", 0, new StringHandler()),
            new BeeTypeEntity(JdbcType.TIMESTAMP, "DATETIME2", Date.class.getName(), "6", 0, new DateHandler()),
            new BeeTypeEntity(JdbcType.TIME, "TIME", java.sql.Time.class.getName(), "0", 0, new DateHandler()),
            new BeeTypeEntity(JdbcType.BINARY, "BINARY", Byte.class.getName(), "1", 0, new ByteHandler()),
            new BeeTypeEntity(JdbcType.TIMESTAMP, "DATETIME2", LocalDateTime.class.getName(), "6", 0, new LocalDateTimeHandler()),
            new BeeTypeEntity(JdbcType.DATE, "DATE", LocalDate.class.getName(), "0", 0, new LocalDateHandler()),
            new BeeTypeEntity(JdbcType.TIME, "TIME", LocalTime.class.getName(), "1", 0, new LocalTimeHandler()),
            new BeeTypeEntity(JdbcType.VARBINARY, "TINYBLOB", Byte.class.getName(), "4", 0, new ByteHandler()),
            new BeeTypeEntity(JdbcType.LONGVARBINARY, "NVARCHAR(MAX)", Byte.class.getName(), "4", 0, new ByteHandler()),
            new BeeTypeEntity(JdbcType.BLOB, "NVARCHAR(MAX)", java.sql.Blob.class.getName(), "0", 1, new BlobHandler()),
            new BeeTypeEntity(JdbcType.CLOB, "NVARCHAR(MAX)", java.sql.Clob.class.getName(), "0", 1, new ClobHandler()),
            new BeeTypeEntity(JdbcType.OTHER, "VARCHAR", String.class.getName(), "255", 0, new StringHandler())
    ));
    private Map<String, BeeTypeEntity> javaMapping;

    protected AbstractSqlServerProvider() {
        javaMapping = initJavaTypeMap(allMapping);
    }

    public Set<BeeTypeEntity> getMapping() {
        return allMapping;
    }

    @Override
    public Map<JdbcType, BeeTypeEntity> getJdbcMap() {
        return allMapping.stream().collect(Collectors.toMap(BeeTypeEntity::getJdbcType, obj -> obj, (key1, key2) -> key1));
    }

    @Override
    public Map<String, BeeTypeEntity> getJavaMap() {
        return javaMapping;
	}

	@Override
	public String getCreateTable(BaseEntity entiry) {
		return "CREATE TABLE %s ( " + buildFiledSql(entiry, BeeDataSourceType.SQLSERVER_WRAP) + " );\r\n";
	}

	@Override
	public String getAutoIncrement(boolean primary) {
		if(primary){
			return " primary key ";
		}
		return "";
	}

	@Override
	public String getTableStruct(String tabName) {
		//select top 1 * from dict
		return "select top 1 * from " + tabName;
	}
	@Override
	public String getTableStructDetail(ArcSharding arc) {
		return null;
	}
	@Override
	public String getTabName(ArcSharding arc) {
        //username如果不是dbo结尾，不加
        if (arc.userName().endsWith(".dbo")) {
            return arc.userName() + "." + arc.table();
        } else {
            return arc.table();
        }
	}



	@Override
	public String getAlertAdd(boolean isUpdate) {
		return " ,";
	}

    /**
     * sqlserver 不支持 comment
     *
     * @param comment
     * @return
     */
	@Override
	public String getComment(String comment) {
//		if (comment.isEmpty()) {
			return "";
//		}
//        else {
//			return " COMMENT '" + comment + "'";
//		}
	}

	@Override
	public String getAlertDrop(List<String> fieldNames) {
		StringBuilder sb = new StringBuilder();
		for(String fieldName : fieldNames){
			sb.append(" drop column ").append(fieldName).append(",");
		}
		return sb.toString();
	}

	@Override
	public String trancationField(Object o, String condition) {
		if(o instanceof Date){
			SimpleDateFormat sdf = new SimpleDateFormat(TimeUtils.DATE_FORMAT_DATETIME);
			return " '"+ sdf.format(TimeUtils.parse(o.toString()))+ "'";
		}
		return null;
	}

	@Override
    public String getFiledLenght(String clazz, String length) {
        try {
            if (Class.forName(clazz.replace("class", "").trim()).isAssignableFrom(Integer.class)
                    || Class.forName(clazz.replace("class", "").trim()).isAssignableFrom(Clob.class)
                    || Class.forName(clazz.replace("class", "").trim()).isAssignableFrom(Blob.class)){
				return "";
			}
        } catch (ClassNotFoundException e) {
			BizLogger.error(e);
		}
		return "("+length+")";
	}

    public String insertBaseList(Map<String, Object> para) {
        List list = (List) para.get("list");
        StrUtils.notNull(list, "list is null");
        String tableName = TableUtils.getTableName((BaseEntity) list.get(0));
        StringBuilder sql = new StringBuilder();
        sql.append("<script>");
        sql.append("insert into ");
        sql.append(tableName);
        //需要去除id字段
        String fields = getInsertFiled((BaseEntity) list.get(0));
        sql.append("(").append(fields).append(")");
        sql.append(" VALUES ");
        sql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" separator=\",\">");
        BaseEntity t = (BaseEntity) list.get(0);
        sql.append("(");
        sql.append(getInsertFiledValue(t));
        sql.append(")");
        sql.append("</foreach>");
        sql.append("</script>");
        return sql.toString();
    }

    public static String getInsertFiled(BaseEntity t) {
        List<String> getField = new ArrayList<>();
        List<Field> fields = SqlProvider.getValidTableFiled(t, true);
        for (Field f : fields) {
            Id id = f.getAnnotation(Id.class);
            if (null != id) {
                continue;
            }
            Object o = ReflectionUtils.getFieldValue(t, f.getName());
            getField.add(SqlProvider.getCloumName(f));
        }
        return StrUtils.join(getField, ",");
    }

    public static String getInsertFiledValue(BaseEntity t) {
        List<String> sql = new ArrayList<>();
        List<Field> fields = SqlProvider.getValidTableFiled(t, true);
        for (Field f : fields) {
            Id id = f.getAnnotation(Id.class);
            if (null != id) {
                continue;
            }
            Object o = ReflectionUtils.getFieldValue(t, f.getName());
            if (ObjectUtils.isNotEmpty(o)) {
                sql.add(SqlProvider.getFiledType("#{item.", t, f));
            } else {
                sql.add("#{item." + f.getName() + ",jdbcType=VARCHAR}");
            }
        }
        return StrUtils.join(sql, ",");
    }
}
